package cn.piflow

import java.util.concurrent.atomic.AtomicInteger

import cn.piflow.local.{EventFromProcessor, ProcessorEvent}
import cn.piflow.util.FormatUtils

import scala.collection.mutable.ArrayBuffer

class FlowNode(val id: Int, val processor: Processor) {
}

class FlowLink(val source: FlowNode, val target: FlowNode, val portNamePair: (String, String)) {

}

class FlowGraph {
	val nodes = ArrayBuffer[FlowNode]();
	val links = ArrayBuffer[FlowLink]();
	val nodeIdSerial = new AtomicInteger(0);

	def createNode(processor: Processor): FlowNode = {
		val nid = nodeIdSerial.incrementAndGet();
		val node = new FlowNode(nid, processor);
		nodes += node;
		node;
	}

	def link(thisNode: FlowNode, thatNode: FlowNode,
	         portNamePair: (String, String) = ("_1", "_1")): FlowGraph = {
		links += new FlowLink(thisNode, thatNode, portNamePair);
		this;
	}

	def show() {
		val data = links
			.map { link: FlowLink ⇒
				val startNodeId = link.source.id;
				val endNodeId = link.target.id;
				val startNode = link.source.processor.toString();
				val endNode = link.target.processor.toString();
				val (lable1, lable2) = link.portNamePair;
				Seq[Any](startNodeId -> endNodeId, s"$startNodeId->$endNodeId", startNode, lable1, lable2, endNode)
			}.sortBy(_.apply(0).asInstanceOf[(Int, Int)]).map(_.drop(1));

		FormatUtils.printTable(Seq("", "source node", "out port", "in port", "target node"), data);
	}
}

trait Processor {
	protected var _processorContext: ProcessorContext = null;

	def context = _processorContext;

	//TODO: should not invoked by processor itself
	def notifyEvent(event: ProcessorEvent) = _processorContext.notifyEvent(
		EventFromProcessor(_processorContext.flowNodeId, event));

	def init(ctx: ProcessorContext): Unit = {
		_processorContext = ctx;
	}

	def getInPortNames(): Seq[String];

	def getOutPortNames(): Seq[String];

	//TODO: use Countable traits
	def performN2N(inputs: Map[String, _]): Map[String, _];

	def DEFAULT_IN_PORT_NAMES(n: Int): Seq[String] = {
		(1 to n).map("_" + _);
	}

	def DEFAULT_OUT_PORT_NAMES(n: Int): Seq[String] = {
		(1 to n).map("_" + _);
	}

	def destroy(): Unit = {}
}